`include "PRV564Config.v"
`include "PRV564Define.v"
//////////////////////////////////////////////////////////////////////////////////////////////////
//  Date    : 2021/7/5                                                                          //
//  Author  : Jack.Pan                                                                          //
//  Desc    : 16-Entry TLB core for PRV564 processor, which is used in ATU                      //
//  Version : 0.0(Orignal)                                                                      //
//////////////////////////////////////////////////////////////////////////////////////////////////
module TLBcore#(
	parameter TLB_entry_NUM = 16				//参数化的表项个数
)(
//global signals
    input wire          TLBi_CLK,               //Clock input
    input wire          TLBi_ARST,              //Async reset input(Active High)
//control signals
    input wire          TLBi_access,            //access enable
    input wire          TLBi_writeCHK,          //access and write check
    input wire          TLBi_refersh,           //refersh the TLB
    input wire          TLBi_replace,           //replace enable
//
    input wire [43:0]   TLBi_PPN,               //New PPN input
    input wire [9:0]    TLBi_PTE,               //New PTE input
    input wire [1:0]    TLBi_PageSize,          //New PTE's Page size
    input wire [26:0]   TLBi_VPN,               //Virtual Page Number input
//
    output wire [43:0]  TLBo_PPN,               //Physical Page Number output
    output wire [9:0]   TLBo_PTE,               //Page Table output
    output wire         TLBo_miss               //TLB miss happen

);
//TLB entrys output 
    wire [TLB_entry_NUM - 1:0] Entryo_valid;
	wire [43:0]                Entryo_PPN_tem [TLB_entry_NUM - 1:0] ;	//中间值
	wire [9:0]                 Entryo_PTE_tem [TLB_entry_NUM - 1:0] ;	//中间值	
    wire [43:0]                Entryo_PPN     [TLB_entry_NUM - 1:0];
    wire [9:0]                 Entryo_PTE     [TLB_entry_NUM - 1:0];     
	wire [43:0]                TLBo_PPN_tem   [TLB_entry_NUM - 1:0];
	wire [9:0]                 TLBo_PTE_tem   [TLB_entry_NUM - 1:0];            
    wire [TLB_entry_NUM - 1:0] Entryo_hit;
//TLB entrys input
    wire [TLB_entry_NUM - 1:0] Entryi_replace;
//TLB replace counter
    reg  [7:0]                 Entry_replace_counter;
//generate 16 TLB entry
    genvar i;
    generate 
        for(i = 0; i < TLB_entry_NUM; i=i+1)
            begin:TLBEntrys
            TLB_entry TLB_entry(
                .CLK            (TLBi_CLK),
                .ARST           (TLBi_ARST),
                .refersh        (TLBi_refersh),
                .access         (TLBi_access),
                .write          (TLBi_writeCHK),
                .VPNi           (TLBi_VPN),
                .valid          (Entryo_valid[i]),
                .PTE_out        (Entryo_PTE_tem[i]),
                .PPN_out        (Entryo_PPN_tem[i]),		
                .hit            (Entryo_hit[i]),            
                .PPN_in         (TLBi_PPN),
                .PTE_in         (TLBi_PTE),
                .PTEsize_in     (TLBi_PageSize),
                .replace        (Entryi_replace[i])
            );
			assign Entryo_PPN[i] = (Entryo_hit[i]) ? Entryo_PPN_tem[i] : 44'd0;
			assign Entryo_PTE[i] = (Entryo_hit[i]) ? Entryo_PTE_tem[i] : 10'd0;					
			end	
    endgenerate

//output mux
//PPN
genvar k,l;
generate 
	for(l=0;l<44;l=l+1)
    begin:TLB_output_PPN_sel1
		assign TLBo_PPN_tem[0][l] = Entryo_PPN[0][l];
		for(k=1;k < TLB_entry_NUM;k=k+1) begin:TLB_output_PPN_sel2
			assign TLBo_PPN_tem[k][l] = TLBo_PPN_tem[k-1][l] | Entryo_PPN[k][l];
			end
		assign TLBo_PPN[l] = TLBo_PPN_tem[TLB_entry_NUM -1][l];
	end
endgenerate

//PTE
genvar m,n;
generate 
	for(n=0;n<10;n=n+1)
    begin:TLB_output_PTW_sel1
		assign TLBo_PTE_tem[0][n] = Entryo_PTE[0][n];
		for(m=1;m < TLB_entry_NUM ; m=m+1) begin:TLB_output_PTW_sel2
			assign TLBo_PTE_tem[m][n] = TLBo_PTE_tem[m-1][n] | Entryo_PTE[m][n];
			end
		assign TLBo_PTE[n] = TLBo_PTE_tem[TLB_entry_NUM -1][n];
		
	end
endgenerate

assign TLBo_miss = TLBi_access & !(|Entryo_hit);                 //if no entry hit, and access is enable, TLB miss signal is enable

//---------------------------------------替换项计数器---------------------------------------
// 替换项计数器从0开始计数，一直计到TLB_Entry_NUM-1，以循环的方式进行替换项选择，整体效果表现为先入先出式的替换
always@(posedge TLBi_CLK or posedge TLBi_ARST)begin
	if(TLBi_ARST) begin
		Entry_replace_counter <= 'h0;
	end
	else if(TLBi_replace)begin
        if(Entry_replace_counter==(TLB_entry_NUM-1))begin       //如果替换项已经到顶，则从0开始
            Entry_replace_counter <= 'h0;
        end
        else begin                                              //替换项没有到顶，继续+1
		    Entry_replace_counter <= Entry_replace_counter + 'h1;
        end
    end
end
//-------------------------------------产生替换信号-----------------------------------------
genvar j;
generate
    for(j=0; j<TLB_entry_NUM; j=j+1)begin:Replace_generate
        assign Entryi_replace[j] = TLBi_replace & (Entry_replace_counter==j);
    end
endgenerate

//NRU Clera signal
//assign NRUbit_set = !(|Entryo_NRUbit);                  //if all the NRU bit is 0, then enable NRUbit set signal
endmodule

module TLB_entry(
//时钟信号
    input wire          CLK,
    input wire          ARST,
//    input wire          NRUbit_set,
//VA-PA通道
//命令通道
    input wire          refersh,
    input wire          access,
    input wire          write,
//地址通道
    input wire [26:0]   VPNi,           //虚拟地址
//TLB_ctrl控制信号
    output reg          valid,
    output reg [9:0]    PTE_out,		//该页表的页表项
    output reg [43:0]   PPN_out,		//物理页号输出
    output reg          hit,            //当前表项命中

    input wire [43:0]   PPN_in,
    input wire [1:0]    PTEsize_in,     //新的页表的页大小
    input wire [9:0]    PTE_in,		    //新的页表

    input wire          replace		    //写页表项
);

    reg [26:0]  VPN;		                //页表VPN
    reg [43:0]  PPN;                        //页表对应的PPN
    reg [1:0]   PageSize;

//缓存有效
always@(posedge CLK or posedge ARST)begin
	if(ARST)begin
		valid	<=	1'b0;
	end
    else if(refersh)begin
        valid <= 1'b0;
    end
    else if(hit & write & !PTE_out[`Sv39_D])begin
        valid <= 1'b0;
    end
	else if(replace)begin
		valid	<=	1'b1;
	end
    
end

always@(posedge CLK or posedge ARST)begin
	if(ARST)begin
		PTE_out 	<=	10'b0;
		PPN		    <=	44'b0;
		VPN			<=	27'b0;
        PageSize    <= 2'b00;
	end
	else if(replace)begin
		PTE_out 	<=	PTE_in;
		PPN		    <=	PPN_in;
		VPN			<=	VPNi;
        PageSize    <= PTEsize_in;
	end
    
end
//产生TLB hit和PPN
always@(*)begin
    if(access & valid)begin
        case(PageSize)
            2'b00 :             //4K 大小的页面
            begin
                hit     = (VPNi==VPN) & !(write & !PTE_out[`Sv39_D]);
                PPN_out = PPN;
            end
            2'b01 :             //2M 大小的页面
            begin
                hit     = (VPNi[26:9]==VPN[26:9]) & !(write & !PTE_out[`Sv39_D]);
                PPN_out = {PPN[43:9],VPNi[8:0]};
            end
            2'b10 :             //1G 大小的页面
            begin
                hit     = (VPNi[26:18]==VPN[26:18]) & !(write & !PTE_out[`Sv39_D]);
                PPN_out = {PPN[43:18],VPNi[17:0]};
            end
            default :
            begin
                hit     = 1'b0;
                PPN_out = PPN;
            end
        endcase
    end
    else begin
        hit     = 1'b0;
        PPN_out = PPN;
    end
end

endmodule
